#pragma once
#ifndef __G_MATH_H__
#define __G_MATH_H__


#include "GUtilities/GTypes.h"

namespace GMath
{
	// Global math functions

	float	SinF(float Radians);
	float	CosF(float Radians);
	float	TanF(float Radians);
	
	float	CoSecF(float Radians);
	float	SecF(float Radians);
	float	CotF(float Radians);
	
	float	ArcSinF(float Radians);
	float	ArcCosF(float Radians);
	float	ArcTanF(float Radians);
	float	ArcTan2F(float Adj, float Opp);
	
	float	DegreeToRadianF(float Degrees);
	float	RadianToDegreeF(float Radians);
	
	float	CeilF(float Value);
	float	FloorF(float Value);
	float	AbsF(float Value);
	float	ModF(float Value, float Base);
	float	LogF(float Value);
	float	Log10F(float Value);
	float	ExpF(float Value);
	float	SqrtF(float Value);
	float	PowF(float Base, float Exp);
	float	SaturateF(float Value);	
	bool	EqualF(float Value1, float Value2);

	template <class T>
	static T Sign(T Value)
	{
		return (Value > (T)0) ? (T)1 : ((Value < (T)0) ? (T)-1 : (T)0);
	}

	template <class T>
	static T NextPower2(T Value)
	{
		GUInt32 Integer = (GUInt32)Value;

		if(Integer <= 1)
		{
			return (T)1;
		}

		Integer = (Integer >> 1)  | Integer;
		Integer = (Integer >> 2)  | Integer;
		Integer = (Integer >> 4)  | Integer;
		Integer = (Integer >> 8)  | Integer;
		Integer = (Integer >> 16) | Integer;

		return (T)(((Integer << 1) + 1) - Integer);
	}

	template <class T>
	static T PrevPower2(T Value)
	{
		GUInt32 Integer = (GUInt32)Value;

		if(Integer <= 1)
		{
			return (T)1;
		}

		Integer = (Integer >> 1)  | Integer;
		Integer = (Integer >> 2)  | Integer;
		Integer = (Integer >> 4)  | Integer;
		Integer = (Integer >> 8)  | Integer;
		Integer = (Integer >> 16) | Integer;

		return (T)(Integer - (Integer >> 1));
	}
		
	template <class T>
	static bool	CheckPower2(T Value)
	{
		GUInt32 Integer = (GUInt32)Value;

		return (Integer & (Integer - 1)) != 0
	}

	template <class T>
	static T Loop(T Lower, T Upper, T Value)
	{
		T Margin = Upper - Lower;
			
		if (Value > Upper)
		{
			T Remain = (Value - Upper) % Margin;
			return Lower + Remain;
		}
		else if (Value < Lower)
		{
			T Remain = (Lower - Value) % Margin;
			return Upper - Remain;
		}
		else
		{
			return Value;
		}
	}

	template <class T>
	static T Clamp(T Lower, T Upper, T Value)
	{
		if (Value > Upper)
		{
			return Upper;
		}
		else if (Value < Lower)
		{
			return Lower;
		}
		else
		{
			return Value;
		}
	}
		
	template <class T>
	static T Lerp(T Value1, T Value2, T Amount)
	{
		return Value1 + (Value2 - Value1) * Amount;
	}
		
	template <class T>
	static void Swap(T& Value1, T& Value2)
	{
		T Temp = Value2;
		Value2 = Value1;
		Value1 = Temp;
	}

	template <class T>
	static T Min(T Value1, T Value2)
	{
		return Value1 > Value2 ? Value2 : Value1;
	}
		
	template <class T>
	static T Min(T Value1, T Value2, T Value3)
	{
		if (Value1 > Value2)
		{
			return Value1 > Value3 ? Value1 : Value3;
		}
		else
		{
			return Value2 > Value3 ? Value2 : Value3;
		}
	}

	template <class T>
	static T Max(T Value1, T Value2)
	{
		return Value1 > Value2 ? Value1 : Value2;
	}

	template <class T>
	static T Max(T Value1, T Value2, T Value3)
	{
		if (Value1 < Value2)
		{
			return Value1 < Value3 ? Value1 : Value3;
		}
		else
		{
			return Value2 < Value3 ? Value2 : Value3;
		}
	}

}

#endif